/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study b. free software.
 */

#pragma once

#include <cstdint>
#include <algorithm>

namespace Zen 
{
	enum class ePixel : int
	{
		None  = 0,
		Grey  = 1,	// grey 1 byte
		GA    = 2,	// grey+alpha 2 bytes
		RGB   = 3,	// 3 bytes
		RGBA  = 4,	// 4 bytes
	};

	/**
	 enum usual color's name in RGB format.
	 */
	enum class eColor : uint32_t
	{
		Black     = 0x000000,
		White     = 0xFFFFFF,
		Grey      = 0x808080,

		Red       = 0xFF0000,
		Orange    = 0xFF7F00,
		Yellow    = 0xFFFF00,
		Green     = 0x00FF00,
		Cyan      = 0x00FFFF,
		Blue      = 0x0000FF,
		Magenta   = 0xFF00FF,
	};

	struct Color3b
	{
		uint8_t r8, g8, b8;
	};

	struct Color4f;

	struct Color4b
	{
		uint8_t r8 = 255, g8 = 255, b8 = 255, a8 = 255;

		float af() const { return a8 / 255.f; }
		float rf() const { return r8 / 255.f; }
		float gf() const { return g8 / 255.f; }
		float bf() const { return b8 / 255.f; }
		operator Color4f();
	};

	struct Color4f
	{
		float red = 1.f, green = 1.f, blue = 1.f, alpha = 1.f;
		operator Color4b();
	};

	// color3b
	inline Color3b Color3bMakeGrey(uint8_t byte)
	{
		return { byte, byte, byte };
	}
	inline Color3b Color3bMakeRGB(uint32_t rgb) // 0xrrggbb
	{
		return {
			(uint8_t)(rgb & 0xff),
			(uint8_t)(rgb >> 8 & 0xff),
			(uint8_t)(rgb >> 16 & 0xff)
		};
	}
	inline Color3b Color3bMake(eColor e)
	{
		return Color3bMakeRGB((uint32_t)e);
	}

	inline Color3b Color3bMake(uint8_t r, uint8_t g, uint8_t b)
	{
		return { r, g, b };
	}

	/* 0xRRGGBBAA */
	inline Color4b Color4b(uint32_t rgba)
	{
		return {
			(uint8_t)(rgba >> 24),
			(uint8_t)(rgba >> 16 & 0xff),
			(uint8_t)(rgba >> 8 & 0xff),
			(uint8_t)(rgba & 0xff),
		};
	}

	/* 0xAARRGGBB */
	inline Color4b Color4bARGB(uint32_t argb)
	{
		return {
			(uint8_t)(argb >> 24),
			(uint8_t)(argb >> 16 & 0xff),
			(uint8_t)(argb >> 8 & 0xff),
			(uint8_t)(argb & 0xff),
		};
	}
	inline Color4b Color4b(Color3b const & rgb, uint8_t a8)
	{
		return { rgb.r8, rgb.g8, rgb.b8, a8 };
	}

	inline Color4b Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0xFF)
	{
		return { r, g, b, a };
	}

	inline Color4b Color4b(eColor e)
	{
		return Color4b(Color3bMake(e), 0xff);
	}

	inline Color4b Color4b(Color4f const & c) {
		return Color4b {
			(uint8_t)std::min(c.red * 255.f, 255.f),
			(uint8_t)std::min(c.green * 255.f, 255.f),
			(uint8_t)std::min(c.blue * 255.f, 255.f),
			(uint8_t)std::min(c.alpha * 255.f, 255.f),
		};
	}


	/** Color4f
	 -- Color4b by 4 float
	 */

	inline Color4f Color4f(float r, float g, float b, float a)
	{
		return { r, g, b, a };
	}
	inline Color4f Color4f(Color4b const & color)
	{
		return {
			color.rf(),
			color.gf(),
			color.bf(),
			color.af(),
		};
	}
	inline Color4f Color4f(eColor e)
	{
		return Color4f(Color4b(e));
	}


	inline float ChannelLerp(float from, float to, float value)
	{
		return from + (to - from) * value;
	}

	inline Color4f Color4fLerp(Color4f const & c0, Color4f const & c1, float value)
	{
		return {
			ChannelLerp(c0.red, c1.red, value),
			ChannelLerp(c0.green, c1.green, value),
			ChannelLerp(c0.blue, c1.blue, value),
			ChannelLerp(c0.alpha, c1.alpha, value)
		};
	}

	inline Color4b::operator Color4f() {
		return Color4f(*this);
	}

	inline Color4f::operator Color4b() {
		return Color4b(*this);
	}

}


/// operator

inline bool operator == (Zen::Color3b const & a, Zen::Color3b const & b)
{
	return a.r8 == b.r8 && a.g8 == b.g8 && a.b8 == b.b8;
}

inline bool operator != (Zen::Color3b const & a, Zen::Color3b const & b)
{
	return !(a == b);
}

inline bool operator == (Zen::Color4b const & a, Zen::Color4b const & b)
{
	return *(int32_t*)(void*)&a == *(int32_t*)(void*)&b;
}

inline bool operator != (Zen::Color4b const & a, Zen::Color4b const & b)
{
	return !(a == b);
}

inline void operator *= (Zen::Color4f & a, Zen::Color4f const & b)
{
	a.red *= b.red;
	a.green *= b.green;
	a.blue *= b.blue;
	a.alpha *= b.alpha;
}

inline Zen::Color4f operator * (Zen::Color4f const & a, Zen::Color4f const & b)
{
	return { a.red * b.red, a.green * b.green, a.blue * b.blue, a.alpha * b.alpha };
}
inline Zen::Color4f operator + (Zen::Color4f const & a, Zen::Color4f const & b)
{
	return { a.red + b.red, a.green + b.green, a.blue + b.blue, a.alpha + b.alpha };
}

inline Zen::Color4f operator - (Zen::Color4f const & a, Zen::Color4f const & b)
{
	return { a.red - b.red, a.green - b.green, a.blue - b.blue, a.alpha - b.alpha };
}

inline bool operator == (Zen::Color4f const & a, Zen::Color4f const & b) {
	return a.red == b.red && a.green == b.green && a.blue == b.blue && a.alpha == b.alpha;
}

inline bool operator != (Zen::Color4f const & a, Zen::Color4f const & b) {
	return !(a == b);
}

